﻿using System;
using System.Text;
using System.Reflection;
using Microsoft.Practices.Unity.Configuration;

using Unity;
using Unity.Interception.PolicyInjection.Pipeline;
using Unity.Interception.PolicyInjection.Policies;
using Unity.Interception.PolicyInjection.MatchingRules;

namespace pri.smilly.Demo {
    public class LoggingHandler : ICallHandler {
        public int Order { get; set; }

        public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext) {
            if (input == null) throw new ArgumentNullException("input");
            if (getNext == null) throw new ArgumentNullException("getNext");

            IMethodReturn result;
            Console.WriteLine("Before Execution");
            result = getNext()(input, getNext);
            if (null != result.Exception)
                Console.WriteLine($"Exception Execution : {result.Exception.Message}");
            else
                Console.WriteLine("After Execution");

            Console.WriteLine("Final Execution");
            return result;
        }
    }

    public class LogingHandlerAttribute : HandlerAttribute {
        public override ICallHandler CreateHandler(IUnityContainer container) {
            return new LoggingHandler() { Order = Order };
        }
    }

    public class ExpressionMatchRule : IMatchingRule {
        private readonly IMatcher matcher; 

        public ExpressionMatchRule(string expression) {
            matcher = new ExpressionMatcher(expression);
        }

        public bool Matches(MethodBase method) {
            return matcher.IsMatch(MethodSinature(method));
        }

        private string MethodSinature(MethodBase method) {
            StringBuilder builder = new StringBuilder();
            builder.Append(method.DeclaringType.Namespace).Append('.')
                .Append(method.DeclaringType.Name).Append('.')
                .Append(method.Name).Append('(');

            foreach (ParameterInfo param in method.GetParameters()) {
                builder.Append(param.ParameterType.Namespace).Append('.').Append(param.ParameterType.Name).Append(',');
            }
            if (method.GetParameters().Length > 0) builder.Remove(builder.Length - 1, 1);
            return builder.Append(')').ToString();
        }
    }

    public interface IMatcher {
        bool IsMatch(string input);
    }

    // 类似于 Spring 表达式匹配规则
    public class ExpressionMatcher : IMatcher { 
        private string _expression;

        public ExpressionMatcher(string expression) {
            _expression = expression;
        }

        public bool IsMatch(string metodSinature) {
            // compare body expression
            if (!Match(BodyItems(_expression), BodyItems(metodSinature))) return false;
            // compare args expression
            string[] argsItems = ArgsItems(_expression);
            if (argsItems.Length == 1 && argsItems[0].Equals("..")) return true;
            return Match(argsItems, ArgsItems(metodSinature));
        }

        private string[] BodyItems(string expression) {
            return expression.Substring(0, expression.IndexOf('(')).Split('.');
        }

        private string[] ArgsItems(string expression) {
            int start = expression.IndexOf('(') + 1;
            int length = expression.IndexOf(')') - start;
            return expression.Substring(start, length).Split(',');
        }

        private bool Match(string[] expressionItems, string[] inputItems) {
            if (inputItems.Length < expressionItems.Length) return false;
            for (int i = 0; i < expressionItems.Length; i++) {
                if (expressionItems[i].Equals("**")) break;
                if (expressionItems[i].Equals("*")) continue;
                if (expressionItems[i].Equals(inputItems[i])) continue;
                return false;
            }

            return true;
        }
    }

    public interface IStudent {
        void Show1();
        void Show2();
    }

    public class Student : IStudent {
        public void Show1() {
            Console.WriteLine("method show1 executing...");
            throw new Exception("method show1 execute error");
        }

        public void Show2() {
            Console.WriteLine("method show2 executing...");
        }
    }

    class Program {
        static void Main(string[] args) {
            IUnityContainer container = new UnityContainer();
            container.LoadConfiguration();

            IStudent student = container.Resolve<IStudent>();
            student.Show2();
            student.Show1();
        }
    }
}
